package com.ksyun.campus.dataserver.controller;

import com.ksyun.campus.dataserver.domain.DeleteBlockRequest;
import com.ksyun.campus.dataserver.domain.ReplicateRequest;
import com.ksyun.campus.dataserver.services.DataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/api")
public class DataController {

    @GetMapping("/v1/data/ping")
    public ResponseEntity<Map<String, Object>> ping() {
        return ResponseEntity.ok(Map.of(
                "status", "ok",
                "nodeId", nodeId,
                "timestamp", System.currentTimeMillis()
        ));
    }

    @GetMapping("/v1/data/health")
    public ResponseEntity<Map<String, Object>> health() {
        try {
            // 获取磁盘容量信息
            java.io.File dataDir = new java.io.File(dataPath);
            long totalSpace = dataDir.getTotalSpace();
            long freeSpace = dataDir.getFreeSpace();
            long usedSpace = totalSpace - freeSpace;
            
            return ResponseEntity.ok(Map.of(
                    "healthy", true,
                    "nodeId", nodeId,
                    "capacityBytes", totalSpace,
                    "usedBytes", usedSpace,
                    "freeBytes", freeSpace,
                    "timestamp", System.currentTimeMillis()
            ));
        } catch (Exception e) {
            log.error("Error getting health info", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of("healthy", false, "error", e.getMessage()));
        }
    }

    @Value("${dataserver.data.path:/data}")
    private String dataPath;

    @Value("${dataserver.node-id:data-server-1}")
    private String nodeId;

    @Autowired
    private DataService dataService;

    @PostMapping("/write")
    public ResponseEntity<Map<String, Object>> writeFile(
            @RequestHeader String fileSystemName,
            @RequestHeader(value = "X-Source-Node", required = false) String sourceNode,
            @RequestParam String path,
            @RequestParam int offset,
            @RequestParam int length,
            @RequestBody byte[] data) {
        try {
            // 参数校验
            if (fileSystemName == null || fileSystemName.isBlank()) {
                return badRequest("Missing required header: fileSystemName");
            }
            if (path == null || path.isBlank()) {
                return badRequest("Missing required parameter: path");
            }
            if (offset < 0 || length < 0) {
                return badRequest("offset and length must be non-negative");
            }
            if (data == null || data.length == 0) {
                return badRequest("Request body cannot be empty");
            }

            log.info("Received write request - fileSystemName: {}, path: {}, offset: {}, length: {}, dataSize: {}",
                    fileSystemName, path, offset, length, data.length);

            Map<String, Object> result = (sourceNode != null && !sourceNode.isEmpty())
                    ? dataService.writeReplicaLocalOnly(data, fileSystemName, path, offset, length)
                    : dataService.write(data, fileSystemName, path, offset, length);

            if (Boolean.TRUE.equals(result.get("success"))) {
                return ResponseEntity.ok(result);
            }
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        } catch (IllegalArgumentException | SecurityException e) {
            log.warn("Bad request in writeFile: {}", e.getMessage());
            return badRequest(e.getMessage());
        } catch (Exception e) {
            log.error("Error in writeFile endpoint", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of("success", false, "message", "Internal server error: " + e.getMessage()));
        }
    }

    @GetMapping("/read")
    public ResponseEntity<byte[]> readFile(
            @RequestHeader String fileSystemName,
            @RequestParam String path,
            @RequestParam int offset,
            @RequestParam int length) {
        try {
            // 参数校验
            if (fileSystemName == null || fileSystemName.isBlank()) {
                return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
            }
            if (path == null || path.isBlank()) {
                return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
            }
            if (offset < 0 || length <= 0) {
                return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
            }
            byte[] data = dataService.read(fileSystemName, path, offset, length);
            if (data != null) {
                return ResponseEntity.ok(data);
            }
            return ResponseEntity.notFound().build();
        } catch (IllegalArgumentException | SecurityException e) {
            log.warn("Bad request in readFile: {}", e.getMessage());
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
        } catch (Exception e) {
            log.error("Error in readFile endpoint", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    @DeleteMapping("/delete")
    public ResponseEntity<Map<String, Object>> deleteFile(
            @RequestHeader String fileSystemName,
            @RequestParam String path) {
        try {
            if (fileSystemName == null || fileSystemName.isBlank()) {
                return badRequest("Missing required header: fileSystemName");
            }
            if (path == null || path.isBlank()) {
                return badRequest("Missing required parameter: path");
            }
            boolean success = dataService.delete(fileSystemName, path);
            if (success) {
                return ResponseEntity.ok(Map.of("success", true, "message", "File deleted successfully"));
            }
            return ResponseEntity.notFound().build();
        } catch (IllegalArgumentException | SecurityException e) {
            log.warn("Bad request in deleteFile: {}", e.getMessage());
            return badRequest(e.getMessage());
        } catch (Exception e) {
            log.error("Error in deleteFile endpoint", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of("success", false, "message", "Failed to delete file: " + e.getMessage()));
        }
    }

    @DeleteMapping("/deleteDir")
    public ResponseEntity<Map<String, Object>> deleteDirectory(
            @RequestHeader String fileSystemName,
            @RequestParam String path,
            @RequestParam(defaultValue = "false") boolean recursive) {
        try {
            if (fileSystemName == null || fileSystemName.isBlank()) {
                return badRequest("Missing required header: fileSystemName");
            }
            if (path == null || path.isBlank()) {
                return badRequest("Missing required parameter: path");
            }
            Map<String, Object> result = dataService.deleteDirectory(fileSystemName, path, recursive);
            if (Boolean.TRUE.equals(result.get("success"))) {
                return ResponseEntity.ok(result);
            }
            String code = String.valueOf(result.getOrDefault("errorCode", ""));
            if ("NOT_FOUND".equals(code)) {
                return ResponseEntity.status(HttpStatus.NOT_FOUND).body(result);
            }
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(result);
        } catch (IllegalArgumentException | SecurityException e) {
            log.warn("Bad request in deleteDirectory: {}", e.getMessage());
            return badRequest(e.getMessage());
        } catch (Exception e) {
            log.error("Error in deleteDirectory endpoint", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of("success", false, "message", "Failed to delete directory: " + e.getMessage()));
        }
    }

    @GetMapping("/list")
    public ResponseEntity<Map<String, Object>> list(
            @RequestHeader String fileSystemName,
            @RequestParam String path) {
        try {
            if (fileSystemName == null || fileSystemName.isBlank()) {
                return badRequest("Missing required header: fileSystemName");
            }
            if (path == null || path.isBlank()) {
                return badRequest("Missing required parameter: path");
            }
            Map<String, Object> result = dataService.listDirectory(fileSystemName, path);
            return ResponseEntity.ok(result);
        } catch (IllegalArgumentException | SecurityException e) {
            log.warn("Bad request in list: {}", e.getMessage());
            return badRequest(e.getMessage());
        } catch (Exception e) {
            log.error("Error in list endpoint", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of("success", false, "message", "Failed to list: " + e.getMessage()));
        }
    }

    @GetMapping("/info")
    public ResponseEntity<Map<String, Object>> getFileInfo(
            @RequestHeader String fileSystemName,
            @RequestParam String path) {
        try {
            if (fileSystemName == null || fileSystemName.isBlank()) {
                return badRequest("Missing required header: fileSystemName");
            }
            if (path == null || path.isBlank()) {
                return badRequest("Missing required parameter: path");
            }
            Map<String, Object> fileInfo = dataService.getFileInfo(fileSystemName, path);
            return ResponseEntity.ok(fileInfo);
        } catch (IllegalArgumentException | SecurityException e) {
            log.warn("Bad request in getFileInfo: {}", e.getMessage());
            return badRequest(e.getMessage());
        } catch (Exception e) {
            log.error("Error in getFileInfo endpoint", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of("error", "Failed to get file info: " + e.getMessage()));
        }
    }

    @GetMapping("/replicaMap")
    public ResponseEntity<Map<String, Object>> getReplicaMap(
            @RequestHeader String fileSystemName,
            @RequestParam String path) {
        try {
            if (fileSystemName == null || fileSystemName.isBlank()) {
                return badRequest("Missing required header: fileSystemName");
            }
            if (path == null || path.isBlank()) {
                return badRequest("Missing required parameter: path");
            }
            Map<String, Object> data = dataService.getReplicaMap(fileSystemName, path);
            return ResponseEntity.ok(data);
        } catch (IllegalArgumentException | SecurityException e) {
            log.warn("Bad request in getReplicaMap: {}", e.getMessage());
            return badRequest(e.getMessage());
        } catch (Exception e) {
            log.error("Error in getReplicaMap endpoint", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of("error", "Failed to get replica map: " + e.getMessage()));
        }
    }



    @GetMapping("/status")
    public ResponseEntity<Map<String, Object>> getStatus() {
        try {
            Map<String, Object> status = new java.util.HashMap<>();
            status.put("nodeId", nodeId);
            status.put("dataPath", dataPath);
            status.put("timestamp", System.currentTimeMillis());
            status.put("status", "running");

            java.io.File dataDir = new java.io.File(dataPath);
            if (dataDir.exists()) {
                long totalSpace = dataDir.getTotalSpace();
                long freeSpace = dataDir.getFreeSpace();
                long usedSpace = totalSpace - freeSpace;

                status.put("totalCapacity", totalSpace);
                status.put("freeCapacity", freeSpace);
                status.put("usedCapacity", usedSpace);
                status.put("usagePercent", (double) usedSpace / totalSpace * 100);
            }
            return ResponseEntity.ok(status);
        } catch (Exception e) {
            log.error("Error getting status", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of("error", "Failed to get status: " + e.getMessage()));
        }
    }

    private ResponseEntity<Map<String, Object>> badRequest(String message) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(Map.of("success", false, "message", message));
    }

    // ===== 与 MetaServer 对齐的块级接口 =====
    @PostMapping("/v1/data/writeBlockWithData")
    public ResponseEntity<Map<String, Object>> writeBlockWithData(
            @RequestParam String fileId,
            @RequestParam int blkIdx,
            @RequestParam(required = false, defaultValue = "false") boolean isPrimary,
            @RequestParam(required = false) String sourceNodeId,
            @RequestBody byte[] data) {
        try {
            if (fileId == null || fileId.trim().isEmpty()) {
                return badRequest("FileId is required");
            }
            if (blkIdx < 0) {
                return badRequest("Valid blkIdx is required (>= 0)");
            }
            if (data == null || data.length == 0) {
                return badRequest("Block data cannot be empty");
            }

            log.info("Received writeBlockWithData request - fileId: {}, blkIdx: {}, size: {}, isPrimary: {}",
                    fileId, blkIdx, data.length, isPrimary);

            Map<String, Object> result = dataService.writeBlock(fileId, blkIdx, data, isPrimary, sourceNodeId);
            if (Boolean.TRUE.equals(result.get("success"))) {
                return ResponseEntity.ok(result);
            }
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        } catch (IllegalArgumentException | SecurityException e) {
            log.warn("Bad request in writeBlockWithData: {}", e.getMessage());
            return badRequest(e.getMessage());
        } catch (Exception e) {
            log.error("Error in writeBlockWithData endpoint", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of("success", false, "message", "Internal server error: " + e.getMessage()));
        }
    }

    @PostMapping("/v1/data/writeBlock")
    public ResponseEntity<Map<String, Object>> writeBlock(
            @RequestParam String fileId,
            @RequestParam int blkIdx,
            @RequestParam(required = false, defaultValue = "false") boolean isPrimary,
            @RequestParam(required = false) String sourceNodeId,
            @RequestBody byte[] data) {
        try {
            if (fileId == null || fileId.trim().isEmpty()) {
                return badRequest("FileId is required");
            }
            if (blkIdx < 0) {
                return badRequest("Valid blkIdx is required (>= 0)");
            }
            if (data == null || data.length == 0) {
                return badRequest("Block data cannot be empty");
            }

            log.info("Received writeBlock request - fileId: {}, blkIdx: {}, size: {}, isPrimary: {}",
                    fileId, blkIdx, data.length, isPrimary);

            Map<String, Object> result = dataService.writeBlock(fileId, blkIdx, data, isPrimary, sourceNodeId);
            if (Boolean.TRUE.equals(result.get("success"))) {
                return ResponseEntity.ok(result);
            }
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        } catch (IllegalArgumentException | SecurityException e) {
            log.warn("Bad request in writeBlock: {}", e.getMessage());
            return badRequest(e.getMessage());
        } catch (Exception e) {
            log.error("Error in writeBlock endpoint", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of("success", false, "message", "Internal server error: " + e.getMessage()));
        }
    }
    @PostMapping("/v1/data/replicate")
    public ResponseEntity<Map<String, Object>> replicateBlock(@RequestBody ReplicateRequest request) {
        try {
            if (request == null || request.getSrc() == null || request.getDst() == null) {
                return badRequest("Invalid request body");
            }
            if (request.getSrc().getHost() == null || request.getSrc().getPort() <= 0
                    || request.getSrc().getFileId() == null || request.getSrc().getBlkIdx() == null) {
                return badRequest("Invalid src endpoint");
            }

            Map<String, Object> result = dataService.replicateBlockFromRemote(
                    request.getSrc().getHost(),
                    request.getSrc().getPort(),
                    request.getSrc().getFileId(),
                    request.getSrc().getBlkIdx(),
                    request.getSize()
            );
            boolean ok = Boolean.TRUE.equals(result.get("success"));
            return ok ? ResponseEntity.ok(result)
                    : ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        } catch (Exception e) {
            log.error("replicateBlock error", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of("success", false, "message", e.getMessage()));
        }
    }

    @PostMapping("/v1/data/deleteBlock")
    public ResponseEntity<Map<String, Object>> deleteBlock(@RequestBody DeleteBlockRequest request) {
        try {
            if (request == null || request.getFileId() == null) {
                return badRequest("fileId is required");
            }
            Map<String, Object> result = dataService.deleteBlock(request.getFileId(), request.getBlkIdx());
            boolean ok = Boolean.TRUE.equals(result.get("success"));
            return ok ? ResponseEntity.ok(result)
                    : ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        } catch (Exception e) {
            log.error("deleteBlock error", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of("success", false, "message", e.getMessage()));
        }
    }

    @GetMapping("/v1/data/block")
    public ResponseEntity<byte[]> readBlock(@RequestParam String fileId, @RequestParam int blkIdx) {
        try {
            byte[] data = dataService.readBlock(fileId, blkIdx);
            if (data == null) return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
            return ResponseEntity.ok(data);
        } catch (Exception e) {
            log.error("readBlock error", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    @PostMapping("/v1/data/writeBlockWithData")
    public ResponseEntity<Map<String, Object>> writeBlockWithData(
            @RequestParam String fileId,
            @RequestParam int blkIdx,
            @RequestParam(defaultValue = "true") boolean isPrimary,
            @RequestParam(value = "sourceNodeId", required = false) String sourceNodeId,
            @RequestBody byte[] data) {
        try {
            if (fileId == null || fileId.trim().isEmpty()) {
                return badRequest("fileId is required");
            }
            if (blkIdx < 0) {
                return badRequest("blkIdx must be non-negative");
            }
            if (data == null || data.length == 0) {
                return badRequest("Block data cannot be empty");
            }

            log.info("Received writeBlockWithData request - fileId: {}, blkIdx: {}, isPrimary: {}, dataSize: {}", 
                    fileId, blkIdx, isPrimary, data.length);

            Map<String, Object> result = dataService.writeBlock(fileId, blkIdx, data, isPrimary, sourceNodeId);
            boolean success = Boolean.TRUE.equals(result.get("success"));
            return success ? ResponseEntity.ok(result) 
                    : ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        } catch (Exception e) {
            log.error("writeBlockWithData error", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of("success", false, "message", e.getMessage()));
        }
    }
}









